{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 实现\n",
    "\n",
    "YUV420-RGB转换规则贫道其他教程。\n",
    "\n",
    "eg:  \n",
    "rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream\n",
    "\n",
    "此处以海康IP摄像头为例，appsink 接收颜色空间为I420的图片，手动转为RGB并保存一帧为本地图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gi\n",
    "\n",
    "gi.require_version('Gst', '1.0')\n",
    "\n",
    "from gi.repository import GObject, Gst\n",
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "Gst.init(None)\n",
    "\n",
    "\n",
    "def I420toRGB(data):\n",
    "    w = 704\n",
    "    h = 576\n",
    "    size = w * h\n",
    "\n",
    "    stream = np.fromstring(data,\n",
    "                           np.uint8)  # convert data form string to numpy array\n",
    "\n",
    "    # Y bytes  will start form 0 and end in size-1\n",
    "    y = stream[0:size].reshape(\n",
    "        h, w)  # create the y channel same size as the image\n",
    "\n",
    "    # U bytes will start from size and end at size+size/4 as its size = framesize/4\n",
    "    u = stream[size:(size + (size // 4))].reshape(\n",
    "        (h // 2), (w // 2))  # create the u channel  itssize=framesize/4\n",
    "\n",
    "    # up-sample the u channel to be the same size as the y channel and frame using pyrUp func in opencv2\n",
    "    u_upsize = cv2.pyrUp(u)\n",
    "\n",
    "    # do the same for v channel\n",
    "    v = stream[(size + (size // 4)):].reshape((h // 2), (w // 2))\n",
    "    v_upsize = cv2.pyrUp(v)\n",
    "\n",
    "    # create the 3-channel frame using cv2.merge func watch for the order\n",
    "    yuv = cv2.merge((y, u_upsize, v_upsize))\n",
    "    cv2.imwrite('yuv.png', yuv)\n",
    "\n",
    "    # Convert TO RGB format\n",
    "    rgb = cv2.cvtColor(yuv, cv2.cv2.COLOR_YCrCb2RGB)\n",
    "    cv2.imwrite('rgb.jpg', rgb)\n",
    "    exit('ok')\n",
    "\n",
    "    # show frame\n",
    "    # cv2.imshow(\"show\", rgb)\n",
    "    cv2.waitKey(1)\n",
    "\n",
    "\n",
    "def on_new_buffer(appsink):\n",
    "    print('new buffer')\n",
    "    sample = appsink.emit('pull-sample')\n",
    "    # get the buffer\n",
    "    buf = sample.get_buffer()\n",
    "    # extract data stream as string\n",
    "    data = buf.extract_dup(0, buf.get_size())\n",
    "    I420toRGB(data)\n",
    "    return 0\n",
    "\n",
    "\n",
    "def Init():\n",
    "    global pipline\n",
    "\n",
    "    CLI = \"rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin !appsink name=sink\"\n",
    "\n",
    "    # simplest way to create a pipline\n",
    "    pipline = Gst.parse_launch(CLI)\n",
    "\n",
    "    # getting the sink by its name set in CLI\n",
    "    appsink = pipline.get_by_name(\"sink\")\n",
    "\n",
    "    # setting some important properties of appsnik\n",
    "    appsink.set_property(\"max-buffers\", 20)  # prevent the app to consume huge part of memory\n",
    "    appsink.set_property('emit-signals', True)  # tell sink to emit signals\n",
    "    appsink.set_property('sync', False)  # no sync to make decoding as fast as possible\n",
    "    appsink.set_property('drop', True)  # Drop old buffers when the buffer queue is filled\n",
    "\n",
    "    appsink.connect('new-sample', on_new_buffer)  # connect signal to callable func\n",
    "    appsink.connect('new-preroll', on_new_prerool)\n",
    "    appsink.connect('eos', on_eos)\n",
    "\n",
    "\n",
    "def on_eos(appsink):\n",
    "    print('on_eos', appsink)\n",
    "    return False\n",
    "\n",
    "\n",
    "def on_new_prerool(appsink):\n",
    "    print('on_new_prerool', appsink)\n",
    "    return False\n",
    "\n",
    "\n",
    "def run():\n",
    "    pipline.set_state(Gst.State.PLAYING)\n",
    "    loop = GObject.MainLoop()\n",
    "    loop.run()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    Init()\n",
    "    run()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 说明\n",
    "\n",
    "当然appsink也可以直接读取到RGB 图像，由Gstreamer的Filter帮忙转换，省区手动转换的过程\n",
    "```sh\n",
    "rtspsrc name=src protocols=tcp location={} latency=10 ! decodebin ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink\n",
    "```\n",
    "\n",
    "```py\n",
    "# 此处只写明主要步骤\n",
    "nd_data = np.frombuffer(RGB_buffer, np.uint8)\n",
    "rgb = nd_data.reshape((height, width, 3))\n",
    "cv2.imwrite('rgb.jpg', rgb)\n",
    "```\n",
    "\n",
    "详细代码可参考本人github 相关项目"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
